//
// Created by wangwanlong on 2024/6/23.
//

#ifndef MCGPP_REFINER_BASE_H
#define MCGPP_REFINER_BASE_H

#include "graph.h"
#include "metrics.h"
#include "randomize.h"

namespace mcgpp {

  //局部搜索
  class RefinerBase {
  public:
    RefinerBase(ds::GenericGraph &graph) : _g(graph),
                                           best_partition(_g.initialNumNodes()),
                                           cut_nodes(graph.initialNumNodes()) {

    }

    const static ll constexpr invalidGain = std::numeric_limits<ll>::max();

    //评估移动节点的收益delta: delta < 0 更好的电导
//    double moveNodeGain(IDType v, PartitionID from, PartitionID to, Metrics &old_obj, Metrics &new_obj) {
//      new_obj.cut = old_obj.cut + _g.node(v).total_edge_weight - 2 * _g.node(v).cut_edge_weight;
//      new_obj.volume = std::min(_g._part_info[from].weight - _g.node(v).weight(),
//                                _g._part_info[to].weight + _g.node(v).weight());
//
//      return new_obj.calCond() - old_obj.conductance;
//
//    }



    ll moveNodeGain(IDType v, PartitionID from, PartitionID to, Metrics &old_obj, Metrics &new_obj) {
      if(_g._part_info[from].t_weight == _g.node(v).weight()){
        //只有一个点不允许移动
        return invalidGain;
      }
      new_obj.cut_num = old_obj.cut_num + _g.node(v).total_edge_num - 2 * _g.node(v).cut_edge_num;
      new_obj.volume_num = std::min(_g._part_info[from].num - _g.node(v).total_edge_num,
                                    _g._part_info[to].num + _g.node(v).total_edge_num);
      new_obj.cut_weight = old_obj.cut_weight + _g.node(v).total_edge_weight - 2 * _g.node(v).cut_edge_weight;
      new_obj.volume_weight = std::min(_g._part_info[from].weight - _g.node(v).total_edge_weight,
                                       _g._part_info[to].weight + _g.node(v).total_edge_weight);
      return new_obj.calCond() - old_obj.conductance;

    }

    bool isBorderNode(IDType u) {
      return _g.node(u).cut_edge_weight > 0;
    }

    void changeNodePart(IDType v, PartitionID from, PartitionID to) {
      _g.changeNodePart(v, from, to, cut_nodes);

    }
    //计算当前实际电导
    double getTrueObj(){
      return (double )current_obj.cut_weight/(double)std::min(_g._part_info[0].t_weight,_g._part_info[1].t_weight);
    }



    //保存最优解
    void saveBestSol(){
      best_obj = current_obj;
      for(auto v : _g._current_node_set)
        best_partition[v] = _g.partID(v);
    }

    virtual void initializeImpl(){

    }
    virtual void refineImpl() {

    }
    //f_best 当前最优解
    virtual void refineImpl(std::vector<IDType>& refine_nodes, Metrics& f_best) {

    }
    virtual void borderDescenctSearch(std::vector<IDType>& refine_nodes){

    }

    void checkDataStructure() {
//      //检查每个点的weight计算
      _g.check();
      //检查切点
      int cut_node_count = 0;
      for (auto v: _g._current_node_set) {
        if (isBorderNode(v)) {
          ++cut_node_count;
          if (!cut_nodes.contains(v))
            ERROREXIT("cut node error!");
        }
      }
      if (cut_node_count != cut_nodes.size())
        ERROREXIT("cut node count error!");
//      //检查解是否有问题
      ll cut_num = 0, cut_weight = 0;
      Vec<ll> partInfo_num(2), partInfo_weight(2), partInfo_t_weight(2);
      for (auto v: _g._current_node_set) {
        int pid = _g.partID(v);
        partInfo_num[pid] += _g.node(v).total_edge_num;
        partInfo_weight[pid] += _g.node(v).total_edge_weight;
        partInfo_t_weight[pid] += _g.node(v).weight();
      }
      for (int i = 0; i < _g._num_edges; ++i) {
        if (_g.edgeIsEnabled(i) && _g._connectivity_sets[i].bit() == 3) {
          cut_weight += _g.edge(i).weight();
          cut_num += 1;
        }
      }
      ll volume = 0;
      switch (Context::instance().objMetric) {
        case ObjMetric::cut_num:
          if (cut_num != current_obj.cut_num) {
            LOG << "cut num:" << cut_num << current_obj.cut_num;
            metrics::conductance(_g);
            ERROREXIT("cut num error!");
          }
          if (partInfo_num[0] != _g._part_info[0].num || partInfo_num[1] != _g._part_info[1].num) {
            LOG << partInfo_num[0] << partInfo_num[1];
            LOG << _g._part_info[0].num << _g._part_info[1].num;
            ERROREXIT("part info num error!");
          }
          {
            volume = std::min(partInfo_num[0], partInfo_num[1]);
            if (volume != current_obj.volume_num) {
              LOG << volume << current_obj.volume_num;
              ERROREXIT("volume num error!");
            }
            ll cond = (static_cast<double>(cut_num) / static_cast<double>(volume)) * 1e10;
            if (cond != current_obj.conductance)
              ERROREXIT("cond error!");
          }
          break;
        case ObjMetric::cut_weight:
          if (cut_weight != current_obj.cut_weight) {
            LOG << "cut weight:" << cut_weight << current_obj.cut_weight;
            metrics::conductance(_g);
            ERROREXIT("cut weight error!");
          }
          if (partInfo_weight[0] != _g._part_info[0].weight || partInfo_weight[1] != _g._part_info[1].weight) {
            LOG << partInfo_weight[0] << partInfo_weight[1];
            LOG << _g._part_info[0].weight << _g._part_info[1].weight;
            ERROREXIT("part info weight error!");
          }
          {
            volume = std::min(partInfo_weight[0], partInfo_weight[1]);
            if (volume != current_obj.volume_weight) {
              LOG << volume << current_obj.volume_weight;
              ERROREXIT("volume weight error!");
            }
            ll cond = (static_cast<double>(cut_weight) / static_cast<double>(volume)) * 1e10;
            if (cond != current_obj.conductance)
              ERROREXIT("cond error!");
          }
          break;
        case ObjMetric::real:
          if (cut_weight != current_obj.cut_weight) {
            LOG << "cut weight:" << cut_weight << current_obj.cut_weight;
            metrics::conductance(_g);
            ERROREXIT("cut weight error!");
          }
          if (partInfo_t_weight[0] != _g._part_info[0].t_weight || partInfo_t_weight[1] != _g._part_info[1].t_weight) {
            LOG << partInfo_t_weight[0] << partInfo_t_weight[1];
            LOG << _g._part_info[0].t_weight << _g._part_info[1].t_weight;
            ERROREXIT("part info t weight error!");
          }
          {
            volume = std::min(partInfo_t_weight[0], partInfo_t_weight[1]);
            if (volume != current_obj.volume_t_weight) {
              LOG << volume << current_obj.volume_t_weight;
              ERROREXIT("volume weight error!");
            }
            ll cond = (static_cast<double>(cut_weight) / static_cast<double>(volume)) * 1e10;
            if (cond != current_obj.conductance)
              ERROREXIT("cond error!");
          }
          break;
        default:
          ERROREXIT("obj metric error!");
      }
    }


    Metrics current_obj;//当前目标结果
    Metrics best_obj;//最好结果
    ds::GenericGraph &_g;
    std::vector<IDType> best_partition;//保存最优分区
    sp::SparseSet<int> cut_nodes;//保存所有切点


  };
}
#endif //MCGPP_REFINER_BASE_H
